package udp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.Map;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.tritonus.share.sampled.AudioSystemShadow;
import org.tritonus.share.sampled.file.AudioOutputStream;
import org.tritonus.share.sampled.file.TAudioFileFormat;

import com.audiobuddy.serializables.Song;
import com.audiobuddy.serializables.SongCommand;

import threads.AcceptStreamRequests;
import threads.MulticastPlaylistThread;
import threads.ReceiveStopBroadcast;

import domain.ClientDomainController;

public class UDPController {

	private static final int PORT = 4445;
	private static final int SEND_SONG_PORT = 5555;
	private static final int SEND_COMMAND_PORT = 6666;
	private static final int GET_SERVER_IP_PORT = 4448;
	private static final int STREAM_SONG_PORT = 8888;
	
	private ClientDomainController dc;
	private DatagramPacket outPacket;
	private byte[] buffer;
	private DatagramPacket inPacket;
	private byte[] ipbuf;
	private Broadcaster broadcaster;
	private static DatagramSocket datagramSocket;
	private static InetAddress host;

	public UDPController(ClientDomainController clientDomainController) {
		this.dc = clientDomainController;
		
		joinMulticastPlaylistGroup();
		acceptStreamRequests();
		receiveStopBroadcast();
	}

	private void receiveStopBroadcast() {
		new ReceiveStopBroadcast(dc).start();
	}

	private void acceptStreamRequests() {
		new AcceptStreamRequests(dc).start();
		
	}

	private void joinMulticastPlaylistGroup() {
		new MulticastPlaylistThread(dc).start();
		
	}

	public void requestCurrentPlaylist() {
		try{

			host = dc.getServerIp();
			datagramSocket = new DatagramSocket();
			datagramSocket.setReuseAddress(true);
			String message = "requesting playlist";
			outPacket = new DatagramPacket(message.getBytes(), message.length(),
					host,PORT);
			datagramSocket.send(outPacket);
			
			buffer = new byte[4096];
			inPacket = new DatagramPacket(buffer, buffer.length);
			datagramSocket.receive(inPacket);
			System.out.println("client=>received the playlist with udp");
			
			ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
			ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(bais));
			dc.setSongs((LinkedList<Song>) ois.readObject());
			dc.setServerIp(inPacket.getAddress());
			
			
			
		}catch (IOException e) {
			System.out.println("IOException on request current playlist: "+ e);
		} catch (ClassNotFoundException e) {
			System.out.println("class not found on receiving playist");
		}finally{
			datagramSocket.close();
		}
	}

	public void addSong(Song song) {
		try
		{
			DatagramSocket songSocket = new DatagramSocket();
			
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
			oos.flush();
			Song[] songs = new Song[1];
			songs[0] = song;
			oos.writeObject(songs);
			oos.flush();
			
			DatagramPacket songPacket = new DatagramPacket(baos.toByteArray(), baos.size(), dc.getServerIp(), SEND_SONG_PORT);
			songSocket.send(songPacket);
			songSocket.close();
			System.out.println("sended song");
		}catch (IOException e) {
			// TODO: handle exception
		}
		
	}

	public void sendSongCommand(SongCommand songCommand) {
		try{
			DatagramSocket commandSocket = new DatagramSocket();
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
			oos.flush();
			oos.writeObject(songCommand);
			oos.flush();
			
			DatagramPacket commandPacket = new DatagramPacket(baos.toByteArray(), baos.size(), dc.getServerIp(), SEND_COMMAND_PORT);
			commandSocket.send(commandPacket);
			commandSocket.close();
			Thread.sleep(500);
			System.out.println("command sent");
		}catch (IOException e) {
			System.out.println("problem on sendSongCommand: " +e);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	public InetAddress getServerIp() {
		try{
			ipbuf = new byte[256];
			DatagramPacket ip = new DatagramPacket(ipbuf, ipbuf.length);
			MulticastSocket mcs = new MulticastSocket(GET_SERVER_IP_PORT);
			InetAddress address = InetAddress.getByName("230.0.0.1");
			mcs.joinGroup(address);
			mcs.receive(ip);
			
			return ip.getAddress();
		}catch (IOException e) {
			System.out.println("could not find server");
		}
		return null;
	}

	public void streamSongToServer(String absolutePath) {
		try{
			DatagramSocket streamSocket = new DatagramSocket();
			broadcaster = new Broadcaster(absolutePath, streamSocket, STREAM_SONG_PORT);
			broadcaster.broadcast();
			/*File file = new File(absolutePath);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
			oos.flush();
			oos.writeObject(file);
			oos.flush();
			
			DatagramPacket streamPacket = new DatagramPacket(baos.toByteArray(), baos.size(),
					dc.getServerIp(), STREAM_SONG_PORT);
			streamSocket.send(streamPacket);
			System.out.println("sending file to server...");
			streamSocket.close();*/
			
		}catch (IOException e) {
			System.out.println("exception on sending file: "+e);
		}
		
	}
	
	class Broadcaster{
		DatagramSocket broadcastSocket;
		int port;
		String filename;
		private boolean broadcasting = true;
		Broadcaster(String filename,DatagramSocket socket,int port){
		       this.broadcastSocket=socket;
		       this.port=port;
		       this.filename=filename;
		   }
		
		public void stopBroadcasting(){
			broadcasting=false;
		}
		
		void broadcast(){
			try{
				File file = new File(filename);
				int EXTERNAL_BUFFER_SIZE = 1500;
				AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
				int readBytes = 0;
				System.out.println("readbytes = " + readBytes);
				byte[] broadcastBuffer = new byte[EXTERNAL_BUFFER_SIZE];
				while(readBytes != -1 && broadcasting){
					readBytes = audioInputStream.read(broadcastBuffer, 0, broadcastBuffer.length);
					DatagramPacket broadcastPacket = new DatagramPacket(broadcastBuffer, broadcastBuffer.length,
							dc.getServerIp(), port);
					broadcastSocket.send(broadcastPacket);
					//System.out.println("broadastPacket sent, readBytes: " + readBytes);
					Thread.currentThread().sleep(8);
				}
				
			}catch (IOException e) {
				// TODO: handle exception
			} catch (UnsupportedAudioFileException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
		}
		
		SourceDataLine getLine(AudioFormat audioFormat) throws LineUnavailableException
		{
		  SourceDataLine res = null;
		  DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
		  res = (SourceDataLine) AudioSystem.getLine(info);
		  res.open(audioFormat);
		  return res;
		}
	}

	public void stopBroadcasting() {
		this.sendSongCommand(new SongCommand(SongCommand.STOP_BROADCASTING, 0));
		stopLocalBroadcasting();
		
	}

	public void stopLocalBroadcasting() {
		if(broadcaster!=null){
			broadcaster.stopBroadcasting();
		}
		
	}
	
	

}
